<?php
// +----------------------------------------------------------------------
// | ThinkCrypto
// +----------------------------------------------------------------------
// | 版权所有 2014~2021 青海云音信息技术有限公司 [ http://www.yyinfos.com ]
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | gitcode 代码仓库：https://gitcode.net/qhweb/ThinkCrypto
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace think\crypto\service;

use think\crypto\service\Crypto;
use think\facade\Cache;
/**
 * 类自动加载器
 * class Loader
 * @package think\crypto\service
 */
class Loader 
{
    // 解密缓存目录
    private static $DECRYPT_CACHE_DIR = '';
    private static ?Crypto $object = null;
    public static function init(): void
    {
        // 设置解密缓存目录为 runtime/decrypted
        self::$DECRYPT_CACHE_DIR = runtime_path('temp');

        // 确保目录存在
        if (!is_dir(self::$DECRYPT_CACHE_DIR)) {
            mkdir(self::$DECRYPT_CACHE_DIR, 0755, true);
        }
        
        // 动态加载自定义函数
        $sapp = app();
        [$dir, $ext] = [$sapp->getBasePath(), $sapp->getConfigExt()];
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST // 控制遍历顺序，便于判断层级
        );
        foreach ($iterator as $info) {
            if ($info->isFile() && $info->getExtension() === 'php') {
                // 获取当前文件在递归中的深度
                $depth = $iterator->getDepth();
                if ($depth < 2 && in_array($info->getBasename(),["sys{$ext}","common{$ext}"])) {
                    self::loaderFile($info->getPathname());
                }
            }
        }
        
        // 注册自定义加载器（在 ThinkPHP 自动加载之前调用）
        spl_autoload_register([self::class, 'autoLoader'], true, true);
    }

    /**
     * 自定义类文件加载器
     *
     * @param string $class
     * @return void
     */
    private static function autoLoader(string $class): void
    {
        //0. 已加载的类跳过
        if(class_exists('\\'.$class)) return;
        // 1. 获取该类对应的文件路径
        $file = self::getClassFilePath($class);
        if (!$file || !file_exists($file)) {
            return; // 文件不存在，交给下一个自动加载器
        }
       
        // 2. 尝试获取解密后的缓存文件路径
        self::loaderFile($file);
    }

    /**
     * 自定义文件加载器
     *
     * @param string $file
     * @return void
     */
    private static function loaderFile(string $file): void
    {
        $cacheFiles = Cache::get('decryptedFile');
        if(empty($cacheFiles) || !is_array($cacheFiles)){
            Cache::set('decryptedFile', []);
        }
        // 1. 获取解密后的缓存文件路径
        $decryptedFile = self::getDecryptedFilePath($file);
        // 2. 缓存文件存在并且最新直接加载
        if (file_exists($decryptedFile) && filemtime($decryptedFile) >= filemtime($file)) {
            require_once $decryptedFile;
        }else{
            $deContent = self::object()->decryptFile($file);
            // 3. 判断是否是加密文件,并解密缓存文件
            if (!empty($deContent)) {
                // 确保目录存在
                $dir = dirname($decryptedFile);
                if (!is_dir($dir)) {
                    mkdir($dir, 0755, true);
                }
                file_put_contents($decryptedFile, $deContent);
                Cache::push('decryptedFile', $decryptedFile);
                require_once $decryptedFile;
            } else {
                return;
            }
        }
    }

    private static function object(): Crypto
    {
        if (self::$object === null) {
            self::$object = new Crypto();
        }
        return self::$object;
    }
    /**
     * 根据类名获取对应的文件路径
     */
    private static function getClassFilePath(string $class): ?string
    {
        $appNamespace = 'app\\';
        if (str_starts_with($class, $appNamespace)) {
            $relativePath = substr($class, strlen($appNamespace));
            $filePath = app()->getBasePath() . str_replace('\\', '/', $relativePath) . '.php';
            
            return realpath($filePath) ?: null;
        }

        return null;
    }

    /**
     * 获取解密后的缓存文件路径
     *
     * @param string $file 加密文件
     * @return string
     */
    private static function getDecryptedFilePath(string $file): string
    {
        $relativePath = ltrim(str_replace([root_path(), '\\'], ['', '/'], $file), '/');
        $relativePath = md5($relativePath).'.php';
        return self::$DECRYPT_CACHE_DIR . $relativePath;
    }
}